package cn.turboinfo.fuyang.api.provider.common.service.impl.custom;

import cn.turboinfo.fuyang.api.domain.common.service.custom.ServiceCustomService;
import cn.turboinfo.fuyang.api.domain.common.service.order.PayOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.order.RefundOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.wechat.WechatPayService;
import cn.turboinfo.fuyang.api.domain.util.CurrencyUnitUtils;
import cn.turboinfo.fuyang.api.domain.util.DateTimeFormatHelper;
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.EntityObjectType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.FundsAccountType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.custom.ServiceCustomStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.*;
import cn.turboinfo.fuyang.api.entity.common.pojo.custom.ServiceCustom;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.PayOrder;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.RefundOrder;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.RefundOrderCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.RefundOrderUpdater;
import cn.turboinfo.fuyang.api.provider.common.repository.database.custom.ServiceCustomDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.custom.ServiceCustomPO;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sunshow.toolkit.core.qbean.helper.service.impl.DefaultQServiceImpl;
import nxcloud.foundation.core.data.support.annotation.EnableSoftDelete;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
@EnableSoftDelete
public class ServiceCustomServiceImpl extends DefaultQServiceImpl<ServiceCustom, Long, ServiceCustomPO, ServiceCustomDAO> implements ServiceCustomService {

    private final PayOrderService payOrderService;

    private final RefundOrderService refundOrderService;

    private final WechatPayService wechatPayService;

    @Override
    @Transactional
    public void receive(Long customId, Long companyId, BigDecimal price, BigDecimal deposit) {
        ServiceCustomPO serviceCustomPO = getEntityWithNullCheckForUpdate(customId);

        if (serviceCustomPO.getCustomStatus() != ServiceCustomStatus.INIT && serviceCustomPO.getCustomStatus() != ServiceCustomStatus.CANCELLED_BY_COMPANY && serviceCustomPO.getCustomStatus() != ServiceCustomStatus.CANCELLED_BY_STAFF) {
            throw new RuntimeException("定制服务状态不正确");
        }

        serviceCustomPO.setCustomStatus(ServiceCustomStatus.PENDING_PAID);
        serviceCustomPO.setCompanyId(companyId);
        serviceCustomPO.setPrice(price);
        serviceCustomPO.setDeposit(deposit);
    }

    @Override
    public List<ServiceCustom> findByStaffId(Long staffId, ServiceCustomStatus status) {
        return convertQBeanToList(dao.findByStaffIdAndCustomStatusOrderByCreatedTimeDesc(staffId, status));
    }

    @Override
    public List<ServiceCustom> findByCompanyId(Long companyId, ServiceCustomStatus status) {
        return convertQBeanToList(dao.findByCompanyIdAndCustomStatusOrderByCreatedTimeDesc(companyId, status));
    }

    @Override
    public List<ServiceCustom> findByCompanyId(Long companyId, Collection<ServiceCustomStatus> statusCollection) {
        return convertQBeanToList(dao.findByCompanyIdAndCustomStatusInOrderByCreatedTimeDesc(companyId, statusCollection));
    }

    @Override
    public List<ServiceCustom> findByShopId(Long shopId, ServiceCustomStatus status) {
        return convertQBeanToList(dao.findByShopIdAndCustomStatusOrderByCreatedTimeDesc(shopId, status));
    }

    @Override
    @Transactional
    public void consumerCancel(Long serviceCustomId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.INIT
                && po.getCustomStatus() != ServiceCustomStatus.PENDING_PAID
                && po.getCustomStatus() != ServiceCustomStatus.PENDING_DISPATCH
                && po.getCustomStatus() != ServiceCustomStatus.WAITING_STAFF_CONFIRM
                && po.getCustomStatus() != ServiceCustomStatus.PENDING_REASSIGNMENT
                && po.getCustomStatus() != ServiceCustomStatus.DISPATCHED_WAITING_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能取消");
        }

        po.setCompanyId(0L);
        po.setStaffId(0L);
        po.setShopId(0L);
        po.setPrice(BigDecimal.ZERO);
        po.setDeposit(BigDecimal.ZERO);
        po.setCustomStatus(ServiceCustomStatus.CANCELLED_BY_CUSTOMER);

        // 生成退款订单
        ServiceOrderRefundStatus refundStatus = generateRefundOrder(serviceCustomId, po.getCompanyId(), "消费者取消订单, 退款");

        po.setRefundStatus(refundStatus);
    }

    @Override
    @Transactional
    public void companyCancel(Long serviceCustomId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.PENDING_PAID
                && po.getCustomStatus() != ServiceCustomStatus.WAITING_STAFF_CONFIRM
                && po.getCustomStatus() != ServiceCustomStatus.PENDING_REASSIGNMENT
                && po.getCustomStatus() != ServiceCustomStatus.DISPATCHED_WAITING_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能取消");
        }

        po.setCompanyId(0L);
        po.setStaffId(0L);
        po.setShopId(0L);
        po.setPrice(BigDecimal.ZERO);
        po.setDeposit(BigDecimal.ZERO);
        po.setCustomStatus(ServiceCustomStatus.CANCELLED_BY_COMPANY);

        // 生成退款订单
        ServiceOrderRefundStatus refundStatus = generateRefundOrder(serviceCustomId, po.getCompanyId(), "企业取消订单, 退款");

        po.setRefundStatus(refundStatus);

    }

    @Override
    @Transactional
    public void staffCancel(Long serviceCustomId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.WAITING_STAFF_CONFIRM
                && po.getCustomStatus() != ServiceCustomStatus.IN_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能取消");
        }

        po.setCompanyId(0L);
        po.setStaffId(0L);
        po.setShopId(0L);
        po.setPrice(BigDecimal.ZERO);
        po.setDeposit(BigDecimal.ZERO);
        po.setCustomStatus(ServiceCustomStatus.CANCELLED_BY_STAFF);

        // 生成退款订单
        ServiceOrderRefundStatus refundStatus = generateRefundOrder(serviceCustomId, po.getCompanyId(), "家政员取消订单, 退款");

        po.setRefundStatus(refundStatus);
    }

    @Override
    @Transactional
    public void submitCustomPostPaid(Long serviceCustomId, Long payOrderId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.PENDING_PAID) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为后付费");
        }

        po.setPayStatus(ServiceOrderPayStatus.POST_PAID);
        // 因为不是真支付了, 不设置支付时间

        payOrderService.checkAndUpdateStatus(payOrderId, PayOrderStatus.POST_PAID, PayOrderStatus.UNPAID);

        transferStatusAfterPaid(po);
    }

    @Override
    @Transactional
    public void submitCustomDepositPaid(Long serviceCustomId, Long payOrderId, BigDecimal paidAmount, LocalDateTime successTime) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.PENDING_PAID) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为定金支付");
        }

        po.setPayStatus(ServiceOrderPayStatus.PRE_PAID);
        po.setPrepaid(paidAmount);
        po.setCompletedTime(successTime);

        payOrderService.checkAndUpdateStatus(payOrderId, PayOrderStatus.PAID, PayOrderStatus.UNPAID);

        transferStatusAfterPaid(po);
    }

    @Override
    @Transactional
    public void dispatchToStaff(Long serviceCustomId, Long staffId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.PENDING_DISPATCH) {
            throw new IllegalArgumentException("订单状态不正确, 不能指派家政服务员");
        }

        po.setStaffId(staffId);
        po.setCustomStatus(ServiceCustomStatus.DISPATCHED_WAITING_SERVICE);
    }

    @Override
    public List<ServiceCustom> findByUserIdAndStaffId(Long userId, Long staffId, ServiceCustomStatus status) {
        return convertQBeanToList(dao.findByUserIdAndStaffIdAndCustomStatus(userId, staffId, status));
    }

    @Override
    @Transactional
    public void startService(Long serviceCustomId, Long staffId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.DISPATCHED_WAITING_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为服务中");
        }

        po.setServiceStartTime(LocalDateTime.now());
        po.setCustomStatus(ServiceCustomStatus.IN_SERVICE);
    }

    @Override
    @Transactional
    public ServiceCustom staffConfirmServiceCompleted(Long serviceCustomId, BigDecimal additionalFee, BigDecimal discountFee) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.IN_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为服务完成");
        }

        po.setCustomStatus(ServiceCustomStatus.SERVICE_COMPLETED);
        po.setServiceEndTime(LocalDateTime.now());

        // 添加额外费用
        po.setAdditionalFee(additionalFee == null ? BigDecimal.ZERO : additionalFee);
        // 添加减免费用
        po.setDiscountFee(discountFee == null ? BigDecimal.ZERO : discountFee);

        // 维护后续状态
        transferStatusAfterPaid(po);

        return convertQBean(po);
    }

    @Override
    @Transactional
    public void staffConfirm(Long serviceCustomId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.WAITING_STAFF_CONFIRM) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为等待服务");
        }

        po.setCustomStatus(ServiceCustomStatus.DISPATCHED_WAITING_SERVICE);
    }

    @Override
    @Transactional
    public void staffReject(Long serviceCustomId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.WAITING_STAFF_CONFIRM) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为待重新派单");
        }

        // 取消选择的家政服务员
        po.setStaffId(0L);
        po.setCustomStatus(ServiceCustomStatus.PENDING_REASSIGNMENT);
    }

    @Override
    @Transactional
    public void payRemainingUnpaid(Long serviceCustomId, Long payOrderId) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);

        if (po.getCustomStatus() != ServiceCustomStatus.SERVICE_COMPLETED) {
            throw new IllegalArgumentException("订单服务未完成, 无法支付");
        }

        if (po.getPayStatus() != ServiceOrderPayStatus.PENDING_PAY) {
            throw new IllegalArgumentException("订单支付状态不为待支付, 无法支付");
        }

        LocalDateTime now = LocalDateTime.now();

        po.setPayStatus(ServiceOrderPayStatus.PAID);
        po.setCompletedTime(now);
        po.setCreditRatingStatus(ServiceOrderCreditRatingStatus.PENDING);
        po.setCustomStatus(ServiceCustomStatus.COMPLETED);

        payOrderService.checkAndUpdateStatus(payOrderId, PayOrderStatus.PAID, PayOrderStatus.UNPAID);

        transferStatusAfterPaid(po);
    }

    @Override
    @Transactional
    public void refreshRefundStatus(Long serviceCustomId, ServiceOrderRefundStatus status) {
        ServiceCustomPO po = getEntityWithNullCheckForUpdate(serviceCustomId);
        po.setRefundStatus(status);
    }

    private void transferStatusAfterPaid(ServiceCustomPO po) {
        switch (po.getCustomStatus()) {
            case PENDING_PAID -> {
                if (po.getPayStatus() == ServiceOrderPayStatus.PRE_PAID
                        || po.getPayStatus() == ServiceOrderPayStatus.PART_PRE_PAID
                        || po.getPayStatus() == ServiceOrderPayStatus.POST_PAID) {
                    if (po.getStaffId() > 0) {
                        // 如果已经指定家政服务员 等待确认
                        po.setCustomStatus(ServiceCustomStatus.WAITING_STAFF_CONFIRM);
                    } else {
                        // 否则等待家政公司派单
                        po.setCustomStatus(ServiceCustomStatus.PENDING_DISPATCH);
                    }
                }
            }
            case SERVICE_COMPLETED -> {
                // 维护支付状态
                if (po.getPrice().add(po.getAdditionalFee())
                        .subtract(po.getPrepaid())
                        .subtract(po.getDiscountFee())
                        .compareTo(BigDecimal.ZERO) == 0) {
                    po.setPayStatus(ServiceOrderPayStatus.PAID);
                    // 没有剩余要支付的, 直接变成已完成
                    LocalDateTime now = LocalDateTime.now();
                    po.setCompletedTime(now);
                    po.setCreditRatingStatus(ServiceOrderCreditRatingStatus.PENDING);
                    po.setCustomStatus(ServiceCustomStatus.COMPLETED);
                } else {
                    po.setPayStatus(ServiceOrderPayStatus.PENDING_PAY);
                }
            }
        }

    }

    private ServiceOrderRefundStatus generateRefundOrder(Long serviceOrderId, Long companyId, String refundReason) {

        // 查询所以支付订单
        List<PayOrder> payOrderList = payOrderService.findByObjectId(EntityObjectType.SERVICE_CUSTOM, serviceOrderId)
                .stream()
                // 过滤已支付和未支付订单
                .filter(it -> PayOrderStatus.PAID.equals(it.getPayOrderStatus()) || PayOrderStatus.UNPAID.equals(it.getPayOrderStatus()))
                .toList();

        ServiceOrderRefundStatus status = payOrderList.size() == 1 && payOrderList.get(0).getPayType().equals(PayType.POST_PAID) ? ServiceOrderRefundStatus.NO_REFUND : ServiceOrderRefundStatus.PROCESSING;

        for (PayOrder it : payOrderList) {
            // 生成退款订单
            if (it.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                log.info("订单金额小于等于0, 不生成退款订单, payOrderId: {}", it.getId());
                continue;
            }
            RefundOrderCreator.Builder builder = RefundOrderCreator.builder()
                    .withCompanyId(companyId)
                    .withWxTransactionId(String.valueOf(it.getId()))
                    .withWxRefundId(StringUtils.EMPTY)
                    .withPayOrderId(it.getId())
                    .withObjectType(EntityObjectType.SERVICE_CUSTOM)
                    .withObjectId(serviceOrderId)
                    .withOrderAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withRefundAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withUserPayAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withUserRefundAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withUserReceivedAccount(StringUtils.EMPTY)
                    .withStatus(RefundStatus.INIT)
                    .withReason(refundReason);

            switch (it.getPayType()) {
                case POST_PAID -> builder
                        .withRefundType(RefundType.NO_REFUND)
                        .withStatus(RefundStatus.NO_REFUND);
                case WECHAT -> builder
                        .withRefundType(RefundType.WECHAT)
                        .withStatus(RefundStatus.PROCESSING);
                case OFFLINE -> builder
                        .withRefundType(RefundType.OFFLINE)
                        .withStatus(RefundStatus.PROCESSING);
            }

            RefundOrder refundOrder = refundOrderService.save(builder.build());

            if (it.getPayType().equals(PayType.WECHAT)) {
                // 微信申请退款
                WxPayRefundV3Result wechatPay = wechatPayService.applyRefund(it.getId(), refundOrder.getOrderAmount(), refundOrder.getRefundAmount(), refundOrder.getId(), refundReason);

                if (wechatPay != null) {
                    RefundOrderUpdater.Builder updateBuilder = RefundOrderUpdater
                            .builder(refundOrder.getId())
                            .withWxTransactionId(wechatPay.getTransactionId())
                            .withWxRefundId(wechatPay.getRefundId())
                            .withUserReceivedAccount(wechatPay.getUserReceivedAccount());

                    switch (wechatPay.getChannel()) {
                        case "ORIGINAL" -> updateBuilder.withRefundChannel(RefundChannelType.ORIGINAL);
                        case "BALANCE" -> updateBuilder.withRefundChannel(RefundChannelType.BALANCE);
                        case "OTHER_BALANCE" -> updateBuilder.withRefundChannel(RefundChannelType.OTHER_BALANCE);
                        case "OTHER_BANKCARD" -> updateBuilder.withRefundChannel(RefundChannelType.OTHER_BANKCARD);
                    }

                    switch (wechatPay.getFundsAccount()) {
                        case "UNSETTLED" -> updateBuilder.withFundsAccount(FundsAccountType.UNSETTLED);
                        case "AVAILABLE" -> updateBuilder.withFundsAccount(FundsAccountType.AVAILABLE);
                        case "UNAVAILABLE" -> updateBuilder.withFundsAccount(FundsAccountType.UNAVAILABLE);
                        case "OPERATION" -> updateBuilder.withFundsAccount(FundsAccountType.OPERATION);
                        case "BASIC" -> updateBuilder.withFundsAccount(FundsAccountType.BASIC);
                    }

                    switch (wechatPay.getStatus()) {
                        case "SUCCESS" -> updateBuilder.withStatus(RefundStatus.SUCCESS)
                                .withSuccessTime(
                                        LocalDateTime.parse(
                                                wechatPay.getSuccessTime(),
                                                DateTimeFormatHelper.wechatDataTime
                                        )
                                );

                        case "CLOSED" -> updateBuilder.withStatus(RefundStatus.CLOSED);
                        case "PROCESSING" -> updateBuilder.withStatus(RefundStatus.PROCESSING);
                        case "ABNORMAL" -> updateBuilder.withStatus(RefundStatus.ABNORMAL);
                    }

                    refundOrderService.update(updateBuilder.build());
                }
            }
        }

        return status;
    }

}
